package concurrent.semaphore;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 信号量实现
 * 信号量由一个整形变量 S 和两个原子操作 PV 组成
 * P(Prolaag，荷兰语尝试减少)：信号量值减 1，如果信号量值小于 0，则说明资源不够用的，把进程加入等待队列
 * V (Verhoog，荷兰语增加)：信号量值加 1，如果信号量值小于等于 0，则说明等待队列里有进程，那么唤醒一个等待进程
 */
public class Semaphore {
    public Semaphore(AtomicInteger shareNum, int threadNum) {
        this.shareNum = shareNum;
        this.threadQueue = new ArrayBlockingQueue<Thread>(threadNum);
    }

    //假设想要访问的资源数量有3个
    private volatile AtomicInteger shareNum;
    //假设队列最多放入10个线程去等待竞争资源
    private Queue<Thread> threadQueue;

    //P操作,获取资源
    public void P() {
        //先减少资源，在判断资源数
        int num = shareNum.decrementAndGet();
        Thread thread = Thread.currentThread();
        System.err.println(thread.getName()+":正在获取资源，当前资源数为"+num);
        if (num < 0) {
            System.err.println(thread.getName()+":资源数为" + num + ",不够,进入队列中等待");
            threadQueue.add(thread);
        } else {
            System.err.println(thread.getName()+":获取到了该资源");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            V();
        }
    }

    //V操作,释放资源
    public void V() {
        //先增加资源，在判断资源数
        int andIncrement = shareNum.incrementAndGet();

        System.err.println(Thread.currentThread().getName() + ":正在归还资源,数量为" + andIncrement);
        if (andIncrement <= 0) {
            Thread poll = threadQueue.poll();
            //小于1,说明队列中还有线程在等待资源，从队列中唤醒某个线程去拿到资源
            System.err.println(Thread.currentThread().getName() + ":从队列中将" + poll.getName() + "唤醒去拿资源");
            awake(poll);
        }
    }

    public void awake(Thread thread){
        System.err.println(thread.getName()+":获取到了该资源");
        int andIncrement = shareNum.incrementAndGet();
        System.err.println(thread.getName() + ":正在归还资源,数量为" + andIncrement);
        if (andIncrement <= 0) {
            Thread poll = threadQueue.poll();
            //小于1,说明队列中还有线程在等待资源，从队列中唤醒某个线程去拿到资源
            System.err.println(thread.getName() + ":从队列中将" + poll.getName() + "唤醒去拿资源");
            awake(poll);
        }
    }

    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(new AtomicInteger(1), 3);
        for(int i=0; i<3; i++){
            Thread thread = new Thread(() -> {
                System.err.println(Thread.currentThread().getName()+"正在处理资源");
                semaphore.P();
            },"线程" + i);
            thread.start();
        }
    }
}



